#!/usr/local/bin/php -q

<?php
/******************************************************************************
 Version:  0.01-Beta (1st September 2006)
 Author:  R2Hosting, Melbourne. Australia
 Contact: info@r2hosting.com.au
 FileName: autochangeJOSPassword.php

Test Environment:
 PHP 4.4.2
 Joomla! 1.0.10 / 1.0.11 (Using jos_ table prefix)
 Joomla! 1.0.11 (using mos_ table prefix, upgraded from Mambo 4.5.2)
 FC3, FC4 & CentOS 3.8
 Windows Server 2003 / Windows XP

 As of today, (1st September 2006) beta tested modifying upto 10 databases concurrently
 with same and multiple password changes. Currently in production with 5 Joomla! (no Mambo)
 databases. Use at your risk, Test Test, and Test again, with generating single & multiple 
 passwords, updating single and multiple databases. DON'T take my word for it.

Important Note:
 PHP script Execution Time or Memory Usage settings and/or speed and/or server/MySQL load may
 become an issue as the number of database instances is increased. As mentioned above, we have
 not extensively tested this script on more than 10 database instances. Also, PHP is not my 1st
 language, so this script is probably pretty rough and certainly not optimised. PHP was the chosen
 language for this script due to Joomla! being a PHP based application and we assumed that most of 
 its community would also be PHP based folks, plus I need to get my hand in more with PHP.


Information/Overview:
 This is a quick and (reasonably) dirty script to automatically change Joomla!
 or Mambo Administrator passwords on a regular basis using cron.

 This script will generate a single or multiple passwords then change the 
 Administrator's account password for each defined Joomla!/Mambo database.
 Each instance may have the same password or a different password assigned
 to it. For emergency recovery, the old passwords MD5 checksum is also 
 displayed, this may be pasted back in to the appropriate password field
 in MySQL to return the password to its previous state.

 The script uses the Administrator's Account NAME, not the username/login name.
 This provides functionality even if you have modified the login name for
 security reasons from the default of "admin".

 Generated passwords and details can be view by terminal console output (if run manually)
 or email output or both. If run via cron turning off console output will reduce load a 
 little and adding " >& /dev/null " so as not to output the resultant passwords in to 
 logfiles or emailing to the server root for no reason.
 
 Originally posted in the Joomla Forums, please post any modifications or
 improvements to assist others in the promotion, execution and securing of Joomla 
 and the Open Source cause.
*******************************************************************************
 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later 
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
 more details.
******************************************************************************/



/*
** Enter The Table prefix (only one, in this version. either, or)
** Installation defaults are: jos = Joomla!,  mos = Mambo
*/
  $tablePrefix                  =       "jos_";   

/*
** Enter your admin account NAME (not admin account loginname), if you change the Administrator name, enter that here
** Installation default is: Administrator
*/
  $adminAccountName     =       "Administrator";

/*
** Enter your databases here
** Example of multiple database entries.
** $joomlaInstances    =       array("user1_joomladev","user2_joomlaprod");
*/
  $joomlaInstances      =       array("user1_joomladev");


/*
** If multiple databases with different MySQL user accounts
**   Enter your MySQL (root) account and password here
** If a single user database
**   Enter the MySQL database username and password here
*/
  $dbUser                       =       "DBusername";
  $dbPass                       =       "DBpassword";

/*
** Do you want all instances to have the same password?
**   0 = each instance will have its own password
**   1 = each instance will have the same password
*/
  $useSamePass          =       "0";


/*
** What sort of output do you want?
** Terminal console?
** Email to specified user(s)?
**   if so, specify a single or multiple email addresses (comma separated entries)
** Who do you want the email to be sent from?
** What email subject line to you want?
*/
  $consoleOutput        =       "1";  // Default "1"
  $mailOut              =       "1";  // Default "1"
  $adminTo              =       "youremail@yourdomain.com.au";
  $adminFrom            =       "support@yourdomain.com.au";
  $mailSubject          =       "Joomla!/Mambo Auto Password Reset Notification";

/*
** Password is made up of five character position groups " POS1 POS2 POS3 POS4 POS5 "
** The position groups maybe defined in size to increase or decrease the length and complexity of the password
** This provides you with the opportunity to alter the length, complexity and layout of the password.
** Modifying the $characters and/or $delims variables will allow you to alter what characters are used
** in the password and to some degree in which character group.
**
** $sizePOS1, $sizePOS2, $sizePOS3, $sizePOS4, $sizePOS5
**   POS1, POS3 and POS5 select upper/lower case and selected special characters from the $characters variable,
**   depending on the $sizeGRP? variable determines how many characters are selected for each.
**   POS2 and POS5 select special characters only from the $delims variable, depending on the $sizePOSx variable
**   determines how many special characters are selected for each.
**
** The default settings (3,1,2,1,3) will provide a 10 character password, guaranteeing special characters in at
** least 2 position group locations (POS2 and POS3) and will have a mix of upper/lower case and special characters in all 
** all other positions.  EG: A2*#dt!0rE   or   F0d^4G@_dk
** To save confusion, we do NOT use UPPERCASE "O" (OH) or lowercase "l" (el), but we ARE using lowercase "o" (oh),
** UPPERCASE "L" (EL) and "0" Zero.
*/
  $sizePOS1 = "3";
  $sizePOS2 = "1";
  $sizePOS3 = "2";
  $sizePOS4 = "1";
  $sizePOS5 = "3";


/***********************************************************************************************************/
/* END BASIC USER CONFIGURABLE OPTIONS                                                                           */
/* Continue at your risk!                                                                                  */
/***********************************************************************************************************/

?>




<?php

  $today = date("l, jS F Y G:i:s T");
  $hostname = shell_exec('hostname');

     // A Simple function to build POS1, POS3 and POS5 of the password
     function GetRID($x){     

         // You can add or delete characters here for POS1, POS3 and POS5, if you want. (We keep a few special characters for variety here)
         $characters = array("A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "_", "!", "*", "-");
         shuffle($characters);

         for (; strlen($ReqRID)<$x;){
           $ReqRID .= $characters[mt_rand(0, count($characters))];
         }

         return $ReqRID;
     }


     // A Simple function to build POS2 and POS4 of the password
     function GetEXT($x){     

         // You can add or delete special characters here for POS2 and POS4, if you want.
         $delims = array("_", "!", "*", "-", "!", "#", "@", "^");
         shuffle($delims);

         for (; strlen($ReqEXT)<$x;){
           $ReqEXT .= $delims[mt_rand(0, count($delims))];
         }
         
                 return $ReqEXT;        
     }     



/*************************************************
 Only if using the SAME Password for ALL instances
*************************************************/
if ($useSamePass == "1") {

  $ReqRID .= GetRID($sizePOS1);
  $ReqRID .= GetEXT($sizePOS2);
  $ReqRID .= GetRID($sizePOS3);
  $ReqRID .= GetEXT($sizePOS4);
  $ReqRID .= GetRID($sizePOS5);

  $newPassword = "". $ReqRID ."";

    foreach ($joomlaInstances as $instance) {

    // create connection
      $dbConn = mysql_connect("localhost","". $dbUser ."","". $dbPass ."")
            or die("Couldn't make a connection to MySQL");

    // select database
      $db = mysql_select_db("". $instance ."", $dbConn)
            or die("Couldn't select the database ". $instance .".");

      // Make sure we only zap the Administrator named account
      $getAdminDetails      = "SELECT name, password FROM ". $tablePrefix ."users WHERE name = '". $adminAccountName ."'";
      $showAdminDetails     = mysql_query($getAdminDetails);

        while ($adminDetails=mysql_fetch_array($showAdminDetails)) {

          $update_pass = mysql_query("UPDATE ". $tablePrefix ."users SET password = md5('". $newPassword ."') WHERE name = '". $adminAccountName ."'") 
            or die(mysql_error());  

          if ($consoleOutput == "1") {
            echo "\t ----------------------------------------------------------------------\n";
            echo "\t dataBase Instance:\t\t $instance\n";
            echo "\t Previous Administrator MD5:\t ". $adminDetails['password'] ."\n\n";
            echo "\t New Administrator Password:\t $ReqRID\n";
          }
     
          if ($mailOut == "1") {
            $msg .= "<table align='center' border='0' cellpadding='2' cellspacing='0' style='border: 1px solid #C0C0C0; font-family: arial; font-size: 9pt;'>";
            $msg .= "<tr>";
            $msg .= "<tr><td>&nbsp;dataBase Instance</td><td>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td>$instance&nbsp;&nbsp;</td></tr>\n";
            $msg .= "<tr><td style='color: #808080;'>&nbsp;Previous Administrator MD5</td><td style='color: #808080;'>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td style='color: #808080;'>". $adminDetails['password'] ."&nbsp;&nbsp;</td></tr>\n";
            $msg .= "<tr><td>&nbsp;New Administrator Password</td><td>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td style='color: navy;'>$ReqRID&nbsp;&nbsp;</td></tr>\n";
            $msg .= "</table><br>";
          }

        }  // end while

    }  // end foreach

}  // end samePass = 1




/******************************************************
 Only if using the DIFFERENT Password for all instances
******************************************************/
if ($useSamePass == "0") {

  foreach ($joomlaInstances as $instance) {

  // create connection
    $dbConn = mysql_connect("localhost","". $dbUser ."","". $dbPass ."")
          or die("Couldn't make a connection to MySQL.");

  // select database
    $db = mysql_select_db("". $instance ."", $dbConn)
          or die("Couldn't select the database ". $instance .".");

    // Make sure we only zap the Administrator named account
    $getAdminDetails      = "SELECT name, password FROM ". $tablePrefix ."users WHERE name = '". $adminAccountName ."'";
    $showAdminDetails     = mysql_query($getAdminDetails);

    while ($adminDetails=mysql_fetch_array($showAdminDetails)) {

      // Clean up ReqRID
      unset ($ReqRID);

      GetRID($x);
        $ReqRID .= GetRID($sizePOS1);
        $ReqRID .= GetEXT($sizePOS2);
        $ReqRID .= GetRID($sizePOS3);
        $ReqRID .= GetEXT($sizePOS4);
        $ReqRID .= GetRID($sizePOS5);

        $newPassword = "". $ReqRID ."";

        $update_pass = mysql_query("UPDATE ". $tablePrefix ."users SET password = md5('". $newPassword ."') WHERE name = '". $adminAccountName ."'") 
          or die(mysql_error());  

        if ($consoleOutput == "1") {
          echo "\t ----------------------------------------------------------------------\n";
          echo "\t dataBase Instance:\t\t $instance\n";
          echo "\t Previous Administrator MD5:\t ". $adminDetails['password'] ."\n";
          echo "\t New Administrator Password:\t $ReqRID\n";
        }

        if (($mailOut == "1") && ($useSamePass == "0")) {
          $msg .= "<table align='center' border='0' cellpadding='2' cellspacing='0' style='border: 1px solid #C0C0C0; font-family: arial; font-size: 9pt;'>";
          $msg .= "<tr>";
          $msg .= "<tr><td>&nbsp;dataBase Instance</td><td>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td>$instance&nbsp;&nbsp;</td></tr>\n";
          $msg .= "<tr><td style='color: #808080;'>&nbsp;Previous Administrator MD5</td><td style='color: #808080;'>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td style='color: #808080;'>". $adminDetails['password'] ."&nbsp;&nbsp;</td></tr>\n";

          $msg .= "<tr><td>&nbsp;New Administrator Password</td><td>&nbsp;:&nbsp;&nbsp;&nbsp;&nbsp;</td><td style='color: navy;'>$ReqRID&nbsp;&nbsp;</td></tr>\n";
          $msg .= "</table><br>";
        }

    }  // end while

  } // end for each

}  // end samePass = 0



  if ($mailOut == "1") {

    $adminSubject = "". $mailSubject ." - ". $today ."";

    // The Message
    $adminMessage = ("
                      <br><br>
                      <table border='0' align='left' cellpadding='0' cellspacing='0'>
                        <tr>
                          <td width='50'>&nbsp;</td>
					      <td style='border: 1px solid #808080; font-family: arial; font-size: 10pt; background-color: #C0C0C0;' align='center'><b>Joomla/Mambo Auto Password Reset</b></td>
                        </tr>
                        <tr>
                          <td>&nbsp;</td>
                          <td align='center'><br>". $msg ."</td>
                        </tr>
                        <tr>
						  <td>&nbsp;</td>
						  <td style='font-family: arial; font-size: 8pt;'>Effective for <b>". $hostname ."</b><br>As of <b>". $today ."</b></td>
						</tr>
                      </table>
                    ");

    // To send HTML mail, you can set the Content-type header.
    $adminHeaders  = "MIME-Version: 1.0\r\n";
    $adminHeaders .= "Content-type: text/html; charset=iso-8859-1\r\n";
    // additional headers
    $adminHeaders .= "From: ". $adminFrom ."\r\n";

    mail($adminTo, $adminSubject, $adminMessage, $adminHeaders);

  }  // enf if $mailOut = 1


  if ($consoleOutput == "1") {
    echo "\t ----------------------------------------------------------------------\n\n";
      if ($useSamePass == "0") { print "\t useSamePass = No, Generating Diffferent Passwords For All Instances\n"; } else { print "\t useSamePass = Yes, Generating Same Password For All Instances\n"; }
      if ($mailOut == "0") { print "\t emailOut = No, Email Notification Not Requested\n\n\n"; } else { print "\t emailOut = Yes, Email Notification Sent to $adminTo\n\n\n"; }
  }

?>
